/****************************************************************************
 * arch/arm/src/armv8-r/arm_head.S
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.  The
 * ASF licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the
 * License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>

#include <arch/irq.h>

#include "arm.h"
#include "cp15_cacheops.h"
#include "sctlr.h"
#include "arm_internal.h"

	.file	"arm_head.S"

/****************************************************************************
 * Configuration
 ****************************************************************************/

/* There are three operational memory configurations:
 *
 * 1. We execute in place in FLASH (CONFIG_BOOT_RUNFROMFLASH=y).  In this case
 *    the boot logic must:
 *
 *    - Configure SDRAM (if present),
 *    - Initialize the .data section in RAM, and
 *    - Clear .bss section
 *
 * 2. We boot in FLASH but copy ourselves to SDRAM from better performance.
 *    (CONFIG_BOOT_RUNFROMFLASH=n && CONFIG_BOOT_COPYTORAM=y).  In this case
 *    the boot logic must:
 *
 *    - Configure SDRAM (if present),
 *    - Copy ourself to DRAM, and
 *    - Clear .bss section (data should be fully initialized)
 *
 *   In this case, we assume that the logic within this file executes from FLASH.
 *
 * 3. There is bootloader that copies us to SDRAM (but probably not to the beginning)
 *    (CONFIG_BOOT_RUNFROMFLASH=n && CONFIG_BOOT_COPYTORAM=n). In this case SDRAM
 *    was initialized by the boot loader, and this boot logic must:
 *
 *    - Clear .bss section (data should be fully initialized)
 */

/* Beginning (BOTTOM/BASE) and End+1 (TOP) of the IDLE stack.
 *
 * The IDLE stack is the stack that is used during initialization and,
 * eventually, becomes the stack of the IDLE task when initialization
 * is complete.
 *
 * REVISIT:  There are issues here in some configurations.  The stack
 * pointer is initialized very early in the boot sequence.  But in some
 * architectures the memory supporting the stack may not yet be
 * initialized (SDRAM, for example, would not be ready yet).  In that
 * case, ideally the IDLE stack should be in some other memory that does
 * not require initialization (such as internal SRAM)
 */

#ifndef IDLE_STACK_BASE
#  ifdef CONFIG_SMP
#    define IDLE_STACK_BASE _enoinit
#  else
#    define IDLE_STACK_BASE _ebss
#  endif
#endif

#define IDLE_STACK_TOP  IDLE_STACK_BASE+CONFIG_IDLETHREAD_STACKSIZE

/****************************************************************************
 * Global Symbols
 ****************************************************************************/

/* Imported symbols */

	.global	arm_boot			/* Branch to continue initialization in C */

	.global	_sbss				/* Start of .bss in RAM */
	.global	_ebss				/* End+1 of .bss in RAM */

	.global _hyp_vector_start
	.global _sys_vector_start
#ifdef CONFIG_BOOT_RUNFROMFLASH
	.global	_eronly				/* Where .data defaults are stored in FLASH */
	.global	_sdata				/* Where .data needs to reside in SDRAM */
	.global	_edata
#endif
#ifdef CONFIG_ARCH_RAMFUNCS
	.global	_framfuncs			/* Where RAM functions are stored in FLASH */
	.global	_sramfuncs			/* Where RAM functions needs to reside in RAM */
	.global	_eramfuncs
#endif

/* Exported symbols */

	.global	__start				/* Power-up/Reset entry point */
	.global	arm_data_initialize	/* Perform C data initialization */
	.global	g_idle_topstack		/* Top of the initial/IDLE stack */

/****************************************************************************
 * Name: __start
 ****************************************************************************/

/* We assume the bootloader has already initialized most of the h/w for
 * us and that only leaves us having to do some os specific things
 * below.
 */

	.text
	.syntax	unified
	.arm
	.global	__start
	.type	__start, #function

__start:
	/* Get cpuindex, cpu0 continue boot, others wait event from cpu0 */

	mrc		CP15_MPIDR(r0)
	and		r0, r0, #0x3
	cmp		r0, #0
#if defined(CONFIG_SMP) && CONFIG_SMP_NCPUS > 1
	beq		__cpu0_start

#ifdef CONFIG_ARM_BUSY_WAIT
	ldr		r2, =CONFIG_ARM_BUSY_WAIT_FLAG_ADDR
1:
	ldr		r1, [r2, #0]
	cmp		r1, #0
	beq		1b
#else
	wfe
#endif

	cmp		r0, #1
	beq		__cpu1_start
#  if CONFIG_SMP_NCPUS > 2
	cmp		r0, #2
	beq		__cpu2_start
#  endif
#  if CONFIG_SMP_NCPUS > 3
	cmp		r0, #3
	beq		__cpu3_start
#  endif
#  if CONFIG_SMP_NCPUS > 4
	cmp		r0, #4
	beq		__cpu4_start
#  endif

#else
	beq		__cpu0_start

__cpux_wfi:

  /* Clear all pending data access */

	dsb		sy
	wfi
	b		__cpux_wfi

#endif

__cpu0_start:

	/* Make sure that IRQs and FIQs are disabled */

	cpsid	if

	/* Set up the stack pointer and clear the frame pointer. */

	ldr		sp, .Lstackpointer
	mov		fp, #0

	/* Set Hyp/PL2 Vector table base register */
	ldr		r0, .Lhypvectorstart
	mcr		CP15_HVBAR(r0)

	/* Invalidate caches and TLBs.
	 *
	 *   NOTE: "The ARMv7 Virtual Memory System Architecture (VMSA) does not
	 *   support a CP15 operation to invalidate the entire data cache. ...
	 *   In normal usage the only time the entire data cache has to be
	 *   invalidated is on reset."
	 *
	 * The instruction cache is virtually indexed and physically tagged but
	 * the data cache is physically indexed and physically tagged.  So it
	 * should not be an issue if the system comes up with a dirty Dcache;
	 * the ICache, however, must be invalidated.
	 */

	mov		r0, #0
	mcr		CP15_TPIDRPRW(r0)	/* Initialize percpu reg TPIDRPRW */
	mcr		CP15_BPIALL(r0)		/* Invalidate entire branch prediction array */
	mcr		CP15_ICIALLU(r0)	/* Invalidate I-cache */
	mov		r1, CP15_CACHE_INVALIDATE
	bl		cp15_dcache_op_level
	isb

	bl		hsctlr_initialize  /* Init Hyp system control register */

	ldr		r0, =HACTLR_INIT
	mcr		CP15_HACTLR(r0)  /* Enable EL1 access all IMP DEFINED registers */

#ifdef CONFIG_ARCH_FPU
	bl		arm_fpuconfig
#endif

	/* Initialize .bss and .data assumt that RAM that is ready to use. */
	bl		arm_data_initialize

	/* Platform hook for highest EL */
	bl		arm_el_init

    /* Move to PL1 SYS with all exceptions masked */
	mov		r0, #(PSR_MODE_SYS | PSR_I_BIT | PSR_F_BIT | PSR_A_BIT)
	msr		spsr_hyp, r0

	adr		r0, 1f
	msr		elr_hyp, r0
	dsb
	isb
	eret

1:
	/* Set up the stack pointer and clear the frame pointer. */
	ldr		sp, .Lstackpointer
	mov		fp, #0

	/* Set PL1 Vector table base register */
	ldr		r0, .Lsysvectorstart
	mcr		CP15_VBAR(r0)

	bl		sctlr_initialize
	bl		arm_boot

	mov		lr, #0				/* LR = return address (none) */
	b		nx_start			/* Branch to nx_start */

#if defined(CONFIG_ARM_BUSY_WAIT) && defined(CONFIG_SMP)
	ldr		r0, =CONFIG_ARM_BUSY_WAIT_FLAG_ADDR
	mov		r1, #1
	str		r1, [r0]
	dsb		sy
#endif

	/* .text Data */

.Lstackpointer:
	.long	IDLE_STACK_TOP
	.size	__start, .-__start

/***************************************************************************
 * Name: arm_data_initialize
 ***************************************************************************/

	.global	arm_data_initialize
	.type	arm_data_initialize, #function

arm_data_initialize:

	/* Zero BSS */

	adr		r0, .Linitparms
	ldmia		r0, {r0, r1}

	mov		r2, #0
1:
	cmp		r0, r1				/* Clear up to _bss_end_ */
	strcc		r2, [r0], #4
	bcc		1b

#ifdef CONFIG_BOOT_RUNFROMFLASH
	/* If the .data section is in a separate, uninitialized address space,
	 * then we will also need to copy the initial values of the .data
	 * section from the .text region into that .data region.  This would
	 * be the case if we are executing from FLASH and the .data section
	 * lies in a different physical address region OR if we are support
	 * on-demand paging and the .data section lies in a different virtual
	 * address region.
	 */

	adr		r3, .Ldatainit
	ldmia		r3, {r0, r1, r2}

2:
	ldr		r3, [r0], #4
	str		r3, [r1], #4
	cmp		r1, r2
	blt		2b
#endif

#ifdef CONFIG_ARCH_RAMFUNCS
	/* Copy any necessary code sections from FLASH to RAM.  The correct
	 * destination in SRAM is given by _sramfuncs and _eramfuncs.  The
	 * temporary location is in flash after the data initialization code
	 * at _framfuncs
	 */

	adr		r3, .Lfuncinit
	ldmia		r3, {r0, r1, r2}

3:
	ldr		r3, [r0], #4
	str		r3, [r1], #4
	cmp		r1, r2
	blt		3b

#ifndef CONFIG_ARMV8R_DCACHE_DISABLE
	/* Flush the copied RAM functions into physical RAM so that will
	 * be available when fetched into the I-Cache.
	 *
	 * Note that this is a branch, not a call and so will return
	 * directly to the caller without returning here.
	 */

	adr		r3, ..Lramfunc
	ldmia		r3, {r0, r1}
	ldr		r3, =up_clean_dcache
	b		r3
#else
	/* Otherwise return to the caller */

	bx		lr
#endif
#else
	/* Return to the caller */

	bx		lr
#endif

/***************************************************************************
 * Name: hsctlr_initialize
 ***************************************************************************/

	.global	hsctlr_initialize
	.type	hsctlr_initialize, #function

hsctlr_initialize:
	mrc		CP15_HSCTLR(r0)		/* Get Hyp System Control Register */

#if !defined(CONFIG_ARMV8R_DCACHE_DISABLE)
	/* Dcache enable
	 *
	 *   SCTLR_C    Bit 2:  DCache enable
	 */

	orr		r0, r0, #(SCTLR_C)
#endif

#if !defined(CONFIG_ARMV8R_ICACHE_DISABLE)
	/* Icache enable
	 *
	 *   SCTLR_I    Bit 12: ICache enable
	 */

	orr		r0, r0, #(SCTLR_I)
#endif

	mcr		CP15_HSCTLR(r0)		/* Write Hyp System Control Register */

	bx		lr

/***************************************************************************
 * Name: sctlr_initialize
 ***************************************************************************/

	.global	sctlr_initialize
	.type	sctlr_initialize, #function

sctlr_initialize:
	/* Configure the system control register (see sctrl.h) */

	mrc		CP15_SCTLR(r0)		/* Get control register */

	/* Clear bits to reset values.  This is only necessary in situations like, for
	 * example, we get here via a bootloader and the control register is in some
	 * unknown state.
	 *
	 *   SCTLR_M        Bit 0:  MPU enable bit
	 *   SCTLR_A        Bit 1:  Strict alignment disabled
	 *   SCTLR_C        Bit 2:  DCache disabled
	 *   SCTLR_CCP15BEN Bit 5:  CP15 barrier enable
	 *   SCTLR_B        Bit 7:  Should be zero on ARMv7R
	 *
	 *   SCTLR_SW       Bit 10: SWP/SWPB not enabled
	 *   SCTLR_I        Bit 12: ICache disabled
	 *   SCTLR_V        Bit 13: Assume low vectors
	 *   SCTLR_RR       Bit 14: Round-robin replacement strategy.
	 *
	 *   SCTLR_BR       Bit 17: Background Region bit
	 *   SCTLR_DZ       Bit 19: Divide by Zero fault enable bit
	 *   SCTLR_FI       Bit 21: Fast interrupts configuration enable bit
	 *   SCTLR_U        Bit 22: Unaligned access model (always one)
	 *
	 *   SCTLR_VE       Bit 24: Interrupt Vectors Enable bit
	 *   SCTLR_EE       Bit 25: 0=Little endian.
	 *   SCTLR_NMFI     Bit 27: Non-maskable FIQ (NMFI) support
	 *   SCTLR_TE       Bit 30: All exceptions handled in ARM state.
	 */

	/* Clear all configurable bits */

	bic		r0, r0, #(SCTLR_A  | SCTLR_C  | SCTLR_CCP15BEN | SCTLR_B)
	bic		r0, r0, #(SCTLR_SW | SCTLR_I  | SCTLR_V        | SCTLR_RR)
	bic		r0, r0, #(SCTLR_BR | SCTLR_DZ | SCTLR_FI)
	bic		r0, r0, #(SCTLR_VE | SCTLR_EE | SCTLR_NMFI     | SCTLR_TE)

#ifndef CONFIG_SMP
	/* Set bits to enable the MPU
	 *
	 *   SCTLR_M     Bit 0:  Enable the MPU
	 */

	orr		r0, r0, #(SCTLR_M)
#endif

	/* Set configured bits */

#ifdef CONFIG_ARMV8R_ALIGNMENT_TRAP
	/* Alignment abort enable
	 *
	 *   SCTLR_A Bit 1:  Strict alignment enabled
	 */

	orr		r0, r0, #(SCTLR_A)
#endif

#if !defined(CONFIG_ARMV8R_DCACHE_DISABLE) && !defined(CONFIG_SMP)
	/* Dcache enable
	 *
	 *   SCTLR_C    Bit 2:  DCache enable
	 */

	orr		r0, r0, #(SCTLR_C)
#endif

#ifdef CONFIG_ARMV8R_SCTLR_CCP15BEN
	/* Enable memory barriers
	 *
	 *  SCTLR_CCP15BEN Bit 5: CP15 barrier enable
	 */

	orr		r0, r0, #(SCTLR_CCP15BEN)
#endif

#if !defined(CONFIG_ARMV8R_ICACHE_DISABLE) && !defined(CONFIG_SMP)
	/* Icache enable
	 *
	 *   SCTLR_I    Bit 12: ICache enable
	 */

	orr		r0, r0, #(SCTLR_I)
#endif

#ifdef CONFIG_ARMV8R_CACHE_ROUND_ROBIN
	/* Round Robin cache replacement
	 *
	 *   SCTLR_RR   Bit 14: Round-robin replacement strategy.
	 */

	orr		r0, r0, #(SCTLR_RR)
#endif

#ifdef CONFIG_ARMV8R_BACKGROUND_REGION
	/* Allow PL1 access to back region when MPU is enabled
	 *
	 *  SCTLR_BR Bit 17: Background Region bit
	 */

	orr		r0, r0, #(SCTLR_BR)
#endif

#ifdef CONFIG_ARMV8R_DIV0_FAULT
	/* Enable divide by zero faults
	 *
	 *   SCTLR_DZ Bit 19: Divide by Zero fault enable bit
	 */

	orr		r0, r0, #(SCTLR_DZ)
#endif

#ifdef CONFIG_ARMV8R_FAST_INTERRUPT
	/* Fast interrupts configuration enable bit
	 *
	 *   SCTLR_FI Bit 21: Fast interrupts configuration enable bit
	 */

	orr		r0, r0, #(SCTLR_FI)
#endif

#ifdef CONFIG_ENDIAN_BIG
	/* Big endian mode
	 *
	 *   SCTLR_EE       Bit 25: 1=Big endian.
	 */

	orr		r0, r0, #(SCTLR_EE)
#endif

#ifdef CONFIG_ARMV8R_NONMASKABLE_FIQ
	/* Non-maskable FIQ support
	 *
	 *   SCTLR_NMFI Bit 27: Non-maskable FIQ (NMFI) support
	 */

	orr		r0, r0, #(SCTLR_NMFI)
#endif

	/* Then write the configured control register */

	mcr		CP15_SCTLR(r0)			/* Write control reg */
	isb
	.rept		12				/* Some CPUs want want lots of NOPs here */
	nop
	.endr

	/* Return to the caller */
	bx		lr

/***************************************************************************
 * Text-section constants
 ***************************************************************************/

	/* Text-section constants:
	 *
	 *   _sbss is the start of the BSS region (see linker script)
	 *   _ebss is the end of the BSS region (see linker script)
	 *
	 * Typical Configuration:
	 * The idle task stack usually starts at the end of BSS and is of size
	 * CONFIG_IDLETHREAD_STACKSIZE.  The heap continues from there until the
	 * end of memory.  See g_idle_topstack below.
	 */

	.type	.Linitparms, %object
.Linitparms:
	.long	_sbss
	.long	_ebss

.Lhypvectorstart:
	.long	_hyp_vector_start
.Lsysvectorstart:
	.long	_sys_vector_start

#ifdef CONFIG_BOOT_RUNFROMFLASH
	.type	.Ldatainit, %object
.Ldatainit:
	.long	_eronly					/* Where .data defaults are stored in FLASH */
	.long	_sdata					/* Where .data needs to reside in SDRAM */
	.long	_edata
#endif

#ifdef CONFIG_ARCH_RAMFUNCS
	.type	.Lfuncinit, %object
.Lfuncinit:
	.long	_framfuncs				/* Where RAM functions are stored in FLASH */
.Lramfuncs:
	.long	_sramfuncs				/* Where RAM functions needs to reside in RAM */
	.long	_eramfuncs
#endif
	.size	arm_data_initialize, . - arm_data_initialize

/***************************************************************************
 * Data section variables
 ***************************************************************************/

	/* This global variable is unsigned long g_idle_topstack and is
	 * exported from here only because of its coupling to .Lstackpointer
	 * above.
	 */

	.section	.rodata, "a"
	.align	4
	.globl	g_idle_topstack
	.type	g_idle_topstack, object

g_idle_topstack:

	.long	IDLE_STACK_TOP
	.size	g_idle_topstack, .-g_idle_topstack
	.end
